import { EcsSystem } from "./EcsSystem";
import { EcsEntity, EntityType } from "./EcsEntity";
import { EcsEntityGroup } from "./EcsEntityGroup";
import { EcsComponent } from "./EcsComponent";

export abstract class EcsWorld {
	nextEntityGenId: number = 10000;
	/**剩余待计入流逝的时间 */
	public _left_elapsed: number = 0;
	protected _elapsed_per_frame: number = 20;
	protected _exit: boolean = false;

	public entities: { [key: number]: EcsEntity } = {};

	public singletons = [];

	public singletonsMap = new Map<Object, any>();

	private _entity_types: { [key: string]: EntityType } = {};

	protected _recover_entities: EcsEntity[] = [];

	protected _systems: EcsSystem[] = [];
	protected _systemsMap = new Map<Object, EcsSystem>()


	protected _groups: { [key: number]: EcsEntityGroup } = {};

	private _enable: boolean = false;
	public get enable(): boolean {
		return this._enable;
	}
	public set enable(value: boolean) {
		this._enable = value;
	}
	/**累计完成帧数 */
	public frameCount: number = 0;
	public lockFrame: number = -1;

	public startTimeMs: number = 0;
	public get frameRate() { return Math.round(1000 / this._elapsed_per_frame) }
	/**每帧流逝时间（毫秒） */
	public get elapsedMsPerFrame() { return this._elapsed_per_frame }
	/**
	 * 游戏世界以开始时间为现实时间算的当前时间
	 */
	public get curTimeMs(): number {
		return this.startTimeMs + this.frameCount * this.elapsedMsPerFrame;
	}
	/**
	 * 游戏世界已流逝的时间(毫秒)
	 */
	public get elapsedTimeMs(): number {
		return this.frameCount * this.elapsedMsPerFrame;
	}

	public constructor(frameRate: number = 59) {
		this._elapsed_per_frame = Math.round(1000 / frameRate);
		this.initSingletons()
		this.initEntityTypes()
		this.initSystems()
	}
	/**初始化单例对象 */
	protected abstract initSingletons()
	/**初始化实体类型 */
	protected abstract initEntityTypes()
	/**初始化系统 */
	protected abstract initSystems()

	public destory(): void {
		for (let k in this.entities) {
			this.removeEntity(this.entities[k].id);
		}

		for (let i = 0; i < this._systems.length; i++) {
			this._systems[i].destroy(this);
		}
	}

	public exit(): void {
		this._exit = true;
	}

	public isExit(): boolean {
		return this._exit;
	}

	/**注册实体类型 */
	public regEntityType(type: string | number): EntityType {
		if (this._entity_types[type]) {
			throw Error("register duplicate entity type:" + type);
		}
		let _type = new EntityType(type);
		this._entity_types[type] = _type;
		return _type;
	}

	/**添加实体 */
	public addEntity(type: string | number, id = 0, ...args: any[]): EcsEntity {
		if (!id) {
			id = this.nextEntityGenId++
		}
		args.unshift(id, type);
		return this.addEntityWithID.apply(this, args as any);
	}

	/**注册实体组 */
	public addEntityWithID(id: number, type: string | number, ...args: any[]) {
		let _type: EntityType = this._entity_types[type];
		let _entity: EcsEntity = _type.create(this, id, args);
		this.entities[_entity.id] = _entity;
		for (let k in this._groups) {
			let _group: EcsEntityGroup = this._groups[k];
			let _compsID: number = _entity.componentsID;
			if (_group.isMatch(_compsID)) {
				_group.add(this, _entity);
			}
		}
		return _entity;
	}

	/**添加单例 */
	public addSingleton<T = any>(singleton: T): T {
		this.singletonsMap.set(singleton.constructor, singleton)
		this.singletons.push(singleton)
		return singleton;
	}

	/**获取单例 */
	public getSingleton<T extends any>(singletonClz: (new () => T)) {
		return this.singletonsMap.get(singletonClz) as T;
	}

	/**移除实体 */
	public removeEntity(id: number) {
		let _entity: EcsEntity = this.entities[id];
		for (let k in this._groups) {
			let _group: EcsEntityGroup = this._groups[k];
			let _compsID: number = _entity.componentsID;
			if (_group.isMatch(_compsID)) {
				_group.remove(this, _entity);
			}
		}
		if (_entity.destroy()) {
			this._recover_entities.push(_entity);
		}
		//this.entities[id] = null
		delete (this.entities[id])

	}

	public getEntity(id: number): EcsEntity {
		return this.entities[id];
	}

	public getGroupByClz(...clzs: (new () => EcsComponent)[]) {
		return this.getGroup(...clzs.map(clz => EcsComponent.getIdFromClz(clz)));
	}

	public getGroup(...compIds: number[]) {
		let _id = EcsEntityGroup.getGroupID(compIds);
		let _group: EcsEntityGroup = this._groups[_id];
		if (!_group) {
			_group = new EcsEntityGroup(compIds, this.entities);
			this._groups[_id] = _group;
		}
		return _group;
	}


	public addSystem(system: EcsSystem): void {
		if ((system as EcsSystem)) {
			// (system as EntitySystem).SetupGroup(this);
			this._systemsMap.set(system.constructor, system);
			this._systems.push(system as EcsSystem);
		}
	}

	public system<T extends EcsSystem>(systemClz: (new (...args: any) => T)) {
		return this._systemsMap.get(systemClz) as T;
	}

	public synFrame(frame: number): void {
		let _delta_frame = frame - this.frameCount;
		let _delta_elapsed = _delta_frame * 1000 / this.frameRate;
		if (_delta_frame < 0) {
			this._left_elapsed = _delta_elapsed;
		}
		else {
			this.update(_delta_elapsed);
		}
	}

	public update(elapsedMs: number) {
		if (!this.enable) { return; }
		if (this.frameCount == 0) { this.startTimeMs = Date.now() };
		elapsedMs += this._left_elapsed;
		while (elapsedMs - this._elapsed_per_frame > 0) {
			if (this.lockFrame >= 0 && this.lockFrame <= this.frameCount) {
				break;
			}
			elapsedMs -= this._elapsed_per_frame;
			this.tickFrame();
		}
		this._left_elapsed = elapsedMs;

	}

	public tickFrame(): void {
		if (!this.enable || this._exit) { return; }
		for (let i = 0; i < this._systems.length; i++) {
			this._systems[i].updateSystem();
		}
		for (let i = 0; i < this._recover_entities.length; i++) {
			this._recover_entities[i].recover();
		}
		this._recover_entities = [];
		this.frameCount++;
	}

	public __onEnableComponent(entity: EcsEntity, id: number): void {
		if (entity.valid) {
			let _oldID = entity.componentsID - Math.pow(2, id);
			let _newID = entity.componentsID;
			for (let k in this._groups) {
				let _group: EcsEntityGroup = this._groups[k];
				if (_group.isMatch(_newID) && !_group.isMatch(_oldID)) {
					_group.add(this, entity);
				}
			}
		}
	}

	public __onDisableComponent(entity: EcsEntity, id: number): void {
		if (entity.valid) {
			let _oldID = entity.componentsID + Math.pow(2, id);
			let _newID = entity.componentsID;
			for (let k in this._groups) {
				let _group: EcsEntityGroup = this._groups[k];
				if (_group.isMatch(_oldID) && !_group.isMatch(_newID)) {
					_group.remove(this, entity);
				}
			}
		}
	}

	public __onReplaceComponent(entity: EcsEntity, id: number): void {
		if (entity.valid) {
			for (let k in this._groups) {
				let _group: EcsEntityGroup = this._groups[k];
				if (_group.isMatch(entity.componentsID)) {
					_group.replace(entity, id);
				}
			}
		}
	}


	public getComponentOwner(comp: EcsComponent) {
		return this.getEntity(comp.ownerId)
	}

	/**获取两帧之间的时间差(毫秒) */
	msBetweenFrames(startFrame: number, endFrame): number {
		return (endFrame - startFrame) * this._elapsed_per_frame;
	}
}

